﻿using System;
using System.Diagnostics;
using System.Threading;

namespace MDK.Device.Components
{
    public enum RotateRefLocations
    {
        Current,
        Zero,
        Barcode
    }

    public interface IRotator : IPlcDeviceComponent
    {
        void Rotate(double offAngle = 0, RotateRefLocations refLoc = RotateRefLocations.Current);

        bool WaitCompleted();

        int ScanInterval { get; set; }
    }

    public class Rotator : IRotator
    {
        public IPlcDevice Device { get; }

        private readonly PlcAddress _returnZero;
        private readonly PlcAddress _angleCompleted;
        private readonly PlcAddress _barcodeLoc;
        private readonly PlcAddress _angle;
        private readonly PlcAddress _angleTrigger;
        private readonly PlcAddress _scanStop;
        private readonly PlcAddress _zeroStop;
        private readonly PlcAddress _fail;
        private readonly PlcAddress _scanInterval;   // 建议值2
        private DateTime _startTime = DateTime.MinValue;

        public Rotator(IPlcDevice device, PlcAddress returnZero,
            PlcAddress barcodeLoc, PlcAddress angle, PlcAddress angleTrigger, PlcAddress angleCompleted, PlcAddress scanInterval, PlcAddress scanStop, PlcAddress zeroStop, PlcAddress fail)
        {
            Device = device;

            _returnZero = returnZero;
            _angleCompleted = angleCompleted;
            _barcodeLoc = barcodeLoc;
            _angle = angle;
            _angleTrigger = angleTrigger;
            _scanInterval = scanInterval;
            _scanStop = scanStop;
            _zeroStop = zeroStop;
            _fail = fail;
        }

        private double _lastOffAngle;
        private RotateRefLocations _lastRefLoc;

        public void Rotate(double offAngle = 0, RotateRefLocations refLoc = RotateRefLocations.Current)
        {
            _lastOffAngle = offAngle;
            _lastRefLoc = refLoc;
            Device.PLC.WriteInt(_angle, (int)Math.Round(offAngle * 3200 / 360));
            switch (refLoc)
            {
                case RotateRefLocations.Zero:
                    Device.PLC.ResetBit(_angleTrigger);
                    Device.PLC.SetBit(_returnZero);
                    break;
                case RotateRefLocations.Barcode:
                    Device.PLC.ResetBit(_angleTrigger);
                    Device.PLC.SetBit(_barcodeLoc);
                    break;
                default:
                    Device.PLC.ResetBit(_angleTrigger);
                    Device.PLC.SetBit(_angleTrigger);
                    break;
            }
            _startTime = DateTime.Now;
        }

        public bool WaitCompleted()
        {
            while (!Device.PLC.BitIsSet(_angleCompleted))
            {
                Thread.Sleep(10);
                
                while (Device.IsScrammed)
                {
                    if (FlowEventHelper.OnScrammed(Device))
                    {
                        Device.ClearError();
                        Rotate(_lastOffAngle, _lastRefLoc);
                        break;
                    }
                    else
                    {
                        Process.GetCurrentProcess().Kill();
                    }
                }
            }

            var isFail = Device.PLC.BitIsSet(_fail);

            if (isFail)
            {
                Device.PLC.ResetBit(_fail);
            }

            return !isFail;
        }

        public int ScanInterval
        {
            get { return Device.PLC.ReadInt(_scanInterval); }
            set { Device.PLC.WriteInt(_scanInterval, value); }
        }
    }
}